﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright(C) < 1998 - 2024 > Glodon Company Limited
// Copyright 2003-2023 by Autodesk, Inc. 
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

using System.ComponentModel;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;

using Microsoft.Extensions.DependencyInjection;

#if Gdmp || Gnuf
using Glodon.Gdmp.UI;
#elif Gap
using Glodon.Gap.UI;
#endif
using Glodon.Lookup.Core.Contracts;
using Glodon.Lookup.Core.Enums;
using Glodon.Lookup.Core.ModelBase;
using Glodon.Lookup.Lookup.Core;
using Glodon.Lookup.Models;
using Glodon.Lookup.Services.Contracts;
using Glodon.Lookup.ViewModels.Contracts;
using Glodon.Lookup.ViewModels.Objects;
using Glodon.Lookup.ViewModels.Pages.Tracebale;
using Glodon.Lookup.Views.Extensions;
using Glodon.Lookup.Views.Utils;

using Wpf.Ui.Appearance;
using Wpf.Ui.Common;
using Wpf.Ui.Contracts;
using Wpf.Ui.Controls;

using CommunityToolkit.Mvvm.Input;

using Glodon.Lookup.Utils;

namespace Glodon.Lookup.Views.Pages
{
    /// <summary>
    /// Interaction logic for GLookupWindow.xaml
    /// </summary>
    public partial class GLookupWindow : Window, IWindow
    {
        private int _scrollTick;
        public ITabGroupViewModel<SnoopHistoryViewModel> TabGroupViewModel { get; set; }

        private readonly IServiceScope _serviceScope;
        public IServiceProvider Scope => _serviceScope.ServiceProvider;

        public GLookupWindow(IServiceScopeFactory scopeFactory, ISettingsService settingsService)
        {
            Wpf.Ui.Application.MainWindow = this;
#if Gnuf
            if (GlodonAPI.UiApplication.UITheme == UITheme.Dark)
            {
                settingsService.Theme = ThemeType.Dark;
            }
            else
            {
                settingsService.Theme = ThemeType.Light;
            }
#endif
            Theme.Apply(this, settingsService.Theme, settingsService.Background);


            InitializeComponent();
            //This line is used to avoid encountering a strange is sue of XamlBehaviorsWpf,
            //see https://github.com/microsoft/XamlBehaviorsWpf/issues/86 to find more
            _ = new Microsoft.Xaml.Behaviors.DefaultTriggerAttribute(typeof(Trigger), typeof(Microsoft.Xaml.Behaviors.TriggerBase), null);

            _serviceScope = scopeFactory.CreateScope();
            TabGroupViewModel = Scope.GetService<ITabGroupViewModel<SnoopHistoryViewModel>>()!;
            DataContext = TabGroupViewModel;

            var windowController = Scope.GetService<IWindowController>();
            var snackbarService = Scope.GetService<ISnackbarService>();


            (TabGroupViewModel as ISnoopViewModel)!.ExitCommand = new ButtonCommand(() =>
            {
                var apiEventHandler = TabGroupViewModel as IApiEventHandler;
                TabGroupViewModel!.ClearData();
                GlodonAPI.DeregistHandlers(apiEventHandler!);
                Close();
            });

            windowController!.SetControlledWindow(this);
            snackbarService!.SetSnackbarControl(RootSnackbar);
            snackbarService.Timeout = 3000;

            Closing += ClosingWindow;
            Activated += (sender, _) => Wpf.Ui.Application.MainWindow = (Window)sender;
            WindowStartupLocation = WindowStartupLocation.CenterScreen;

            new WindowInteropHelper(this).Owner = (IntPtr)UIApplication.Get().GetApplicationWindowId();
        }
        public void OnGridLoaded(object sender, RoutedEventArgs e)
        {
            try
            {
                if (TabGroupViewModel.CurrentViewModel is { SnoopableObjects: { Count: > 0 } })
                {
                    if (TabGroupViewModel.CurrentViewModel.SnoopableObjects.Any(sn => sn.IsSelected))
                    {
                        _ = (TabGroupViewModel as ISnoopViewModel)!.FetchMembersCommand.ExecuteAsync(null);
                    }
                }
            }
            catch (Exception ex)
            {
                Scope.GetService<ISnackbarService>()!.GetSnackbarControl().ShowAsync("表格加载捕获异常", ex.Message + ex.StackTrace,
                    SymbolRegular.ErrorCircle16, ControlAppearance.Danger);
            }
        }
        private void ClosingWindow(object sender, CancelEventArgs e)
        {
            try
            {
                GlodonAPI.DeregistHandlers((TabGroupViewModel as IApiEventHandler)!);
                TabGroupViewModel.ClearData();
                Hide();
                e.Cancel = true;
            }
            catch (Exception ex)
            {
                ExceptionUtils.ShowExceptionMessage(ex);
            }
        }
        private void UnloadServices(object sender, RoutedEventArgs e)
        {
            _serviceScope.Dispose();
        }
        protected void OnGridMouseLeftButtonUp(object sender, RoutedEventArgs routedEventArgs)
        {
            if (sender is DependencyObject dp)
            {
                var datagrid = VisualUtils.FindVisualParent<System.Windows.Controls.DataGrid>(dp);
                if (datagrid == null || datagrid!.SelectedItems.Count != 1) return;
                (TabGroupViewModel as ISnoopViewModel)!.Navigate((Descriptor)datagrid.SelectedItem);
            }
        }


        protected void OnDataGridScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            if (e.VerticalChange != 0) _scrollTick = Environment.TickCount;
        }


        private void OnGridToolTipOpening(object o, ToolTipEventArgs args)
        {
            //Fixed by the tooltip work in 6.0-preview7 https://github.com/dotnet/wpf/pull/6058 but we use net48

            if (_scrollTick != 0 && Environment.TickCount - _scrollTick < 73) args.Handled = true;
        }


        protected async void OnRowLoaded(object sender, RoutedEventArgs routedEventArgs)
        {
            //Lazy init. 1 ms is enough to display data and start initialising components
            await Task.Delay(1);

            var element = (FrameworkElement)sender;
            Descriptor descriptor;
            switch (element.DataContext)
            {
                case SnoopableObject context:
                    descriptor = context.Descriptor;
                    var treeItem = VisualUtils.FindVisualParent<System.Windows.Controls.TreeViewItem>((DependencyObject)sender);
                    CreateTreeTooltip(descriptor, treeItem);
                    CreateTreeContextMenu(descriptor, treeItem);
                    break;
                case Descriptor context:
                    descriptor = context;
                    CreateGridRowContextMenu(descriptor, element);
                    break;
                default:
                    return;
            }
        }

        private void CreateTreeTooltip(Descriptor descriptor, FrameworkElement row)
        {
            row.ToolTip = new ToolTip
            {
                Content = new StringBuilder()
                    .Append("Type: ")
                    .AppendLine(descriptor.Type)
                    .Append("Value: ")
                    .Append(descriptor.Name)
                    .ToString()
            };

            row.ToolTipOpening += OnGridToolTipOpening;
        }

        private void CreateGridTooltip(Descriptor descriptor, FrameworkElement row)
        {
            string toolTipTitle;
            switch (descriptor.MemberAttributes)
            {
                case MemberAttributes.Property:
                    toolTipTitle = "Property: ";
                    break;
                case MemberAttributes.Extension:
                    toolTipTitle = "Extension: ";
                    break;
                default:
                    toolTipTitle = "Method: ";
                    break;
            }
            var builder = new StringBuilder()
                .Append(toolTipTitle)
                .AppendLine(descriptor.Name)
                .Append("Type: ")
                .AppendLine(descriptor.Value.Descriptor.Type)
                .Append("Value: ")
                .Append(descriptor.Value.Descriptor.Name);

            if (!(descriptor.Value.Descriptor.Description is null))
                builder.AppendLine()
                    .Append("Description: ")
                    .Append(descriptor.Value.Descriptor.Description);

            row.ToolTip = new ToolTip
            {
                Content = builder.ToString()
            };

            row.ToolTipOpening += OnGridToolTipOpening;
        }

        private void CreateTreeContextMenu(Descriptor descriptor, FrameworkElement row)
        {
            var contextMenu = new ContextMenu
            {
                Resources = Resources
            };

            contextMenu.AddMenuItem("CopyMenuItem")
                .SetHeader("复制")
                .SetCommand(descriptor, parameter => Clipboard.SetDataObject(parameter.Name));

            row.ContextMenu = contextMenu;
        }

        private void CreateGridRowContextMenu(Descriptor descriptor, FrameworkElement row)
        {
            var contextMenu = new ContextMenu
            {
                Resources = Resources
            };

            contextMenu.AddMenuItem("CopyMenuItem")
                .SetHeader("复制")
                .SetCommand(descriptor, parameter => Clipboard.SetDataObject($"{parameter.Name}: {parameter.Value.Descriptor.Name}"));

            contextMenu.AddMenuItem("CopyMenuItem")
                .SetHeader("复制值")
                .SetCommand(descriptor, parameter => Clipboard.SetDataObject(parameter.Value.Descriptor.Name))
                .SetAvailability(descriptor.Value.Descriptor.Name is not null);

            row.ContextMenu = contextMenu;
        }

        private void AddShortcuts()
        {
            var command = new RelayCommand(() => { (TabGroupViewModel as ISnoopViewModel)!.RefreshMembersCommand.Execute(null); });
            InputBindings.Add(new KeyBinding(command, new KeyGesture(Key.F5)));
        }

        public void Show(Window window)
        {
            Left = window.Left + 47;
            Top = window.Top + 49;
            Show();
        }

        public void ShowAttached()
        {
            WindowStartupLocation = WindowStartupLocation.CenterScreen;
            Show();
        }

        public void Initialize()
        {
            throw new NotImplementedException();
        }

        private void Grid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            System.Windows.Controls.TreeViewItem groupTreeViewItem = VisualUtils.FindVisualParent<System.Windows.Controls.TreeViewItem>(sender as DependencyObject);
            groupTreeViewItem.IsExpanded = !groupTreeViewItem.IsExpanded;
        }

    }
}
